 <?php

#<editor-fold defaultstate="collapsed" desc="CCore::Using">
CCore::Using('base.utils');
CCore::Using('socket.inc.constants');
CCore::Using('socket.inc.streams.socket');
#</editor-fold>

abstract class ACoreSocket
{
	#<editor-fold defaultstate="collapsed" desc="private">
	/**
	 * @var	string
	 */
	private $_sBuffer;

	/**
	 * @var	CCoreSocketIncStreamsSocket
	 */
	private $_oStream;
	#</editor-fold>
	#<editor-fold defaultstate="collapsed" desc="protected">
	/**
	 * @var	ACoreSocketConfig
	 */
	protected $oConfig;

	/**
	 * @var CCoreBaseLog
	 */
	protected $oLogger;
	
	/**
	 * @var CCoreBaseDevelopments
	 */
	protected $oDevelopments;

	/**
	 * @var	array
	 */
	private $aObjectConfig;
 	#</editor-fold>

	/**
	 * @param	ACoreSocketConfig		$oSocketConfig
	 * @param	CCoreBaseDevelopments	$oBaseDevelopments
	 * @return	ACoreSocket
	 */
	public function __construct(ACoreSocketConfig $oSocketConfig, CCoreBaseDevelopments $oBaseDevelopments)
	{
		CCore::CheckTypes(array('$oSocketConfig', '$noLogger'));

		$this->oConfig = $oSocketConfig;
		$this->oDevelopments = $oBaseDevelopments;
		$this->aObjectConfig = array();

		$this->_sBuffer = '';
		$this->_oStream = new CCoreSocketIncStreamsSocket();
	}

	/**
	 * @return	void
	 */
	function __destruct()
	{
		$this->Disconnect();
	}

	/**
	 * @param	string	$sKey
	 * @param	mixed	$mValue
	 * @return	void
	 */
	public function SetConfig($sKey, $mValue)
	{
		CCore::CheckTypes(array('$sKey', '$mValue'));

		if (isset($this->aObjectConfig[$sKey]))
		{
			$this->aObjectConfig[$sKey] = $mValue;
		}
	}

	/**
	 * @param	string	$sKey
	 * @return	mixed
	 */
	public function GetConfig($sKey)
	{
		CCore::CheckTypes(array('$sKey'));

		return (isset($this->aObjectConfig[$sKey])) ? $this->aObjectConfig[$sKey] : null;
	}

	/**
	 * @param	bool	$bIsSSL = false
	 * @return	bool
	 */
	public function Connect($bIsSSL = false)
	{
		CCore::CheckTypes(array('$bIsSSL'));
		
		if ($this->IsConnected())
		{
			$this->oDevelopments->Write('connection also avalible.', CCoreBaseDevelopments::WARNING);
			return false;
		}

		$sErrorStr = '';
		$iErrorNo = 0;

		$sHost = $this->oConfig->GetValue('Host', 'string');
		$iPort = $this->oConfig->GetValue('Port', 'int');
		$iConnectTimeOut = $this->oConfig->GetValue('ConnectTimeOut', 'int');
		$iSocketTimeOut = $this->oConfig->GetValue('SocketTimeOut', 'int');

		if (function_exists('openssl_open') && $bIsSSL)
		{
			$sHost = 'ssl://'.$sHost;
		}

		$this->oDevelopments->Write('start connect to: '.$sHost.':'.$iPort, CCoreBaseDevelopments::INFO);
		$this->_oStream->Open($sHost, $iPort, $iErrorNo, $sErrorStr, $iConnectTimeOut);
		if (!$this->IsConnected())
		{
			if ($this->_oLogger)
			{
				$this->oDevelopments->Write('can\'t connect to server '.$sHost.':'.$iPort, CCoreBaseDevelopments::WARNING);
				$this->oDevelopments->Write('['.$iErrorNo.'] '.$sErrorStr, CCoreBaseDevelopments::WARNING);
			}
			return false;
		}

		$this->_oStream->SetTimeAndBlocking($iSocketTimeOut, true);
		return true;
	}

	/**
	 * @return	bool
	 */
	public function Disconnect()
	{
		if ($this->IsConnected())
		{
			$this->_oStream->Close();
		}
		$this->_sBuffer = '';
		return true;
	}

	/**
	 * @return	bool
	 */
	public function IsConnected()
	{
		return $this->_oStream->IsConnected();
	}

	/**
	 * @return	string
	 */
	public function GetLastBuffer()
	{
		return trim($this->_sBuffer);
	}


	public function SetStream($oSocketStream)
	{
		$this->_oStream = $oSocketStream;
	}

	/**
	 * @param	string	$sLine
	 * @param	bool	$bIsLog = true
	 * @return	bool
	 */
	protected function putLine($sLine, $bIsLog = true)
	{
		CCore::CheckTypes(array('$sLine', '$bIsLog'));

		if (!$this->IsConnected())
		{
			$this->oDevelopments->Write('connection not avalible.', CCoreBaseDevelopments::WARNING);
			return false;
		}

		$sLine = $sLine.C_SOCKET_CRLF;
		if ($bIsLog)
		{
			$this->oDevelopments->Write('> '.trim($sLine), CCoreBaseDevelopments::INFO);
		}
		
		if (!$this->_oStream->Write($sLine))
		{
			$this->oDevelopments->Write('error while sending "'.CCoreBaseUtils::ShowCRLF($sLine).'".', CCoreBaseDevelopments::WARNING);
			$this->Disconnect();
			return false;
		}

		return true;
	}

	/**
	 * @param	bool	$bIsLog = true
	 * @return	string | false
	 */
	protected function getLine($bIsLog = true)
	{
		CCore::CheckTypes(array('$bIsLog'));
		
		if (!$this->IsConnected())
		{
			$this->oDevelopments->Write('connection not avalible.', CCoreBaseDevelopments::WARNING);
			return false;
		}

		$this->_sBuffer = $this->_oStream->Read();
		if ($bIsLog)
		{
			if (is_string($this->_sBuffer))
			{
				$this->oDevelopments->Write('< '.CCoreBaseUtils::ShowCRLF($this->_sBuffer), CCoreBaseDevelopments::INFO);
			}
			else
			{
				$this->oDevelopments->Write('< Stream return: '.gettype($this->_sBuffer), CCoreBaseDevelopments::WARNING);
			}
		}

		if (!$this->_sBuffer)
		{
			if (false === $this->_sBuffer)
			{
				$this->oDevelopments->Write('fgets response is false.', CCoreBaseDevelopments::WARNING);
				$this->Disconnect();
			}
			else if (strlen($this->_sBuffer) == 0)
			{
				return $this->_sBuffer;
			}
			else
			{
				$aSocketStatus = $this->_oStream->Status();
				if (isset($aSocketStatus['timed_out']) && $aSocketStatus['timed_out'])
				{
					$this->oDevelopments->Write('socket timeout reached during connection.', CCoreBaseDevelopments::WARNING);
					$this->Disconnect();
				}
			}
		}

		return $this->_sBuffer;
	}
}
